﻿//#define debug
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Threading;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO.Compression;
namespace tcpAsyncPacket
{
    internal class replyIfo
    {
        public AutoResetEvent autoEvent { get; set; } = new AutoResetEvent(false);
        public object obj { get; set; }

    }
    internal static class tcpTools
    {
        
        internal static bool genTempFile(string filename,long filesize)
        {
            try
            {
                using (var fs = new FileStream(filename, FileMode.Create))
                {
                    fs.SetLength(filesize);
                    fs.Close();
                }
                return true;
            }
            catch
            {
#if debug
                throw;
#endif
                return false;
            }

        }
        internal static long toLong(this IPEndPoint inip)
        {
            if (inip==null)
            {
                return 0;
            }
            else
            {
                return BitConverter.ToUInt32(inip.Address.GetAddressBytes(), 0) * 100000L + inip.Port;
            }
            
        }
        internal static IPEndPoint toIP(this long insz)
        {
            long port;
            var sz = Math.DivRem(insz, 100000, out port);
            return new IPEndPoint(new IPAddress(sz), (int)port);
        }
        internal static string fileSize(this double size)
        {
            string[] units = new string[] { "B", "KB", "MB", "GB", "TB", "PB" };
            double mod = 1024.0;
            int i = 0;
            while (size >= mod)
            {
                size /= mod;
                i++;
            }
            return Math.Round(size, 2) + units[i];
        }
        internal static byte[] keepAliveValues(uint onOff, uint keepAliveTime, uint keepAliveInterval)
        {
            byte[] values = new byte[12];
            BitConverter.GetBytes(onOff).CopyTo(values, 0);
            BitConverter.GetBytes(keepAliveTime).CopyTo(values, 4);
            BitConverter.GetBytes(keepAliveInterval).CopyTo(values, 8);
            
            return values;
        }
        internal static bool isAlive(this Socket inSocket)
        {
            try
            {
                if ((inSocket.Poll(1000, SelectMode.SelectRead) && (inSocket.Available == 0)) || !inSocket.Connected)
                {
                    return false;
                }
                else
                {
                    return true;
                }
            }
            catch
            {
#if debug
                throw;
#endif
                return false;

            }

        }
        internal static void safeClose(this Socket inSocket)
        {
            try
            {
                inSocket.Shutdown(SocketShutdown.Both);
                inSocket.Close();
            }
            catch (Exception)
            {
#if debug
                throw;
#endif
                if (inSocket!=null)
                {
                    inSocket.Close();
                }

            }

        }
        internal static bool receiveAll(this Socket inSocket,ref byte[] tmpBin, int inlength=-1,SocketFlags inFlag=SocketFlags.None, int lmtSec=0)
        {
            try
            {
                int cd = 0;
                bool isok = false;
                var tpsec = lmtSec * 1000;
                var count = 0;
                if (inlength>tmpBin.Length||inlength==-1)
                {
                    count = tmpBin.Length;
                }
                else
                {
                    count = inlength;
                }

                if (tpsec==0)
                {
                    while (cd != count)
                    {
                        if (!inSocket.isAlive())
                        {
                            break;
                        }
                        cd += inSocket.Receive(tmpBin, cd, count - cd, inFlag);
                        if (cd == count)
                        {
                            isok = true;
                        }
                    }
                }
                else
                {
                    while (cd != count)
                    {
                        if (!inSocket.isAlive())
                        {
                            break;
                        }
                        inSocket.ReceiveTimeout = tpsec;
                        DateTime sttime = DateTime.Now;
                        cd += inSocket.Receive(tmpBin, cd, count - cd, inFlag);
                        var jgct = (DateTime.Now - sttime).Milliseconds;
                        if (cd == count)
                        {
                            isok = true;
                        }
                        else
                        {
                            tpsec = tpsec - jgct;
                            if (tpsec <= 0)
                            {
                                break;
                            }
                        }
                    }
                }


                return isok;
            }
            catch
            {
#if debug
                throw;
#endif

                return false;
            }


        }
        internal static bool sendAll(this Socket inSocket,byte[] tmpBin, int inlength=-1,SocketFlags inFlag=SocketFlags.None, int lmtSec = 0)
        {
            try
            {
                int cd = 0;
                bool isok = false;
                var tpsec = lmtSec * 1000;
                var count = 0;
                if (inlength > tmpBin.Length || inlength == -1)
                {
                    count = tmpBin.Length;
                }
                else
                {
                    count = inlength;
                }
                if (tpsec==0)
                {
                    while (cd != count)
                    {
                        if (!inSocket.isAlive())
                        {
                            break;
                        }
                        cd += inSocket.Send(tmpBin, cd, count - cd, inFlag);
                        if (cd == count)
                        {
                            isok = true;
                        }
                    }
                }
                else
                {
                    while (cd != count)
                    {
                        if (!inSocket.isAlive())
                        {
                            break;
                        }
                        inSocket.SendTimeout = tpsec;
                        DateTime sttime = DateTime.Now;
                        cd += inSocket.Send(tmpBin, cd, count - cd, inFlag);
                        var jgct = (DateTime.Now - sttime).Milliseconds;
                        if (cd == count)
                        {
                            isok = true;
                        }
                        else
                        {
                            tpsec = tpsec - jgct;
                            if (tpsec <= 0)
                            {
                                break;
                            }
                        }
                    }
                }

                return isok;


            }
            catch 
            {
#if debug
                throw;
#endif

                return false;
            }


        }
        internal static bool sendAllPack(this Socket inSocket,byte[] tmpBin)
        {
            if (inSocket.sendAll(BitConverter.GetBytes(tmpBin.LongLength)))
            {
                return inSocket.sendAll(tmpBin);
            }
            else
            {
                return false;
            }
        }
    }
    public enum fileType
    {
        receive,
        send
    } 
    public enum comType
    {
        main, p2p,obj, file
    }
    internal class comInfo
    {
        public Guid id { get; set; } = Guid.Empty;
        public int usertoken { get; set; } = -1;
        public comType cType { get; set; } = comType.obj;
        public long mainIP { get; set; } = 0;
        public fileType fType { get; set; } = fileType.send;
        public long fileSize { get; set; } = 0;
        public string fileName { get; set; }
        public object obj { get; set; }
        
    }
    public class netArg : EventArgs
    {
        public IPEndPoint endp { get; }
        public bool isFile { get; set; } = false;
        public bool isOk { get; set; }
        public object obj { get; set; }
        public int userToken { get; set; }
        public object argReply { get; set; } = null;
        public netArg()
        {

        }
        public netArg(IPEndPoint inip, bool iFile, bool rOk, object inobj, int inToken = -1)
        {
            endp = inip;
            isFile = iFile;
            isOk = rOk;
            obj = inobj;
            userToken = inToken;
        }
    }
    public class fileArg : EventArgs
    {
        public IPEndPoint endp { get; set; }
        public fileType fileState { get; set; } = fileType.send;
        public string fileName { get; set; }
        public double value { get; set; }
        public string str { get; set; }
        public int userToken { get; set; }

    }
    public enum infoType
    {
        rmt_init,
        rmt_recComplete,
        rmt_recObj,
        rmt_recFile,
        rmt_sendStart,
        rmt_sendObj,
        rmt_sendFile,
        rmt_sendComplete,
        srv_serverStart,
        srv_accept,
        srv_getReply,
        srv_sendObj,
        srv_getAllClients,
        srv_shut,
        srv_closeClient,
        srv_closeAllClients,
        cli_connect,
        cli_connectAsync,
        cli_connectComplete,
        cli_recComplete,
        cli_recObj,
        cli_sendfile,
        cli_sendStart,
        cli_sendObj,
        cli_sendComplete,
        cli_getReply,
        cli_sendPlan,
        cli_p2pAccept,
        cli_p2pRec,
        cli_getfile,
        cli_getP2pSrv,
        cli_sendObjP2P,
        cli_getReplyP2P,
        cli_sendfileP2P
    }
    public class infoArg : EventArgs
    {
        public IPEndPoint endp { get; set; }
        public infoType iType { get; set; }
        public string message { get; set; }
    }
    public class tcpAsyncServer
    {
        private class remoteClient
        {
            private Socket workSocket=null;
            public long mainIP { get; private set; } = 0;
            public long p2pIP { get; private set; }
            private tcpAsyncServer fl;
            public IPEndPoint remoteIP { get; private set; }
            private bool ismanstop = false;
            private ConcurrentQueue<comInfo> cachesend= new ConcurrentQueue<comInfo>();
            private ReaderWriterLockSlim removewrite = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
            private bool isSending = false;
            private SocketAsyncEventArgs recarg;
            private SocketAsyncEventArgs sdarg;
            
            public remoteClient(Socket insocket,tcpAsyncServer inFl)
            {
                try
                {
                    ismanstop = false;
                    fl = inFl;
                    workSocket = insocket;
                    workSocket.IOControl(IOControlCode.KeepAliveValues, tcpTools.keepAliveValues(1, 1000, 1000), null);
                    remoteIP = workSocket.RemoteEndPoint as IPEndPoint;
                    sdarg = new SocketAsyncEventArgs();
                    sdarg.Completed += Sdarg_Completed;
                    recarg = new SocketAsyncEventArgs();
                    recarg.SetBuffer(new byte[8], 0, 8);
                    recarg.Completed += Recarg_Completed;
                    workSocket.ReceiveAsync(recarg);
                }
                catch (Exception ex)
                {

#if debug
                    throw;
#endif
                    close();
                    if (!ismanstop&&mainIP==0)
                    {
                        callEvent(fl.clientOut, new infoArg { endp = remoteIP, iType = infoType.rmt_init,message=ex.Message });
                    }
                    callEvent(fl.errorRec, new infoArg { endp = remoteIP, iType = infoType.rmt_init,message=ex.Message });
                }

            }
            private void callEvent<T>(EventHandler<T> inevt,T inarg,bool intask = false) where T:EventArgs
            {
                try
                {
                    if (intask)
                    {
                        if (inevt != null)
                        {
                            Task.Factory.StartNew(() =>
                            {
                                inevt(fl, inarg);
                            });
                        }

                    }
                    else
                    {
                        inevt?.Invoke(fl, inarg);
                    }
                }
                catch
                {
#if debug
                    throw;
#endif
                    return;
                }
            }
            private void Recarg_Completed(object sender, SocketAsyncEventArgs e)
            {
                try
                {
                    if (!workSocket.isAlive())
                    {
                        close();
                        if (!ismanstop&&mainIP==0)
                        {
                            callEvent(fl.clientOut, new infoArg { endp = remoteIP, iType = infoType.rmt_recComplete });
                        }
                    }
                    else
                    {
                        if (e.SocketError==SocketError.Success&&e.BytesTransferred==8)
                        {
                            var length = BitConverter.ToInt64(e.Buffer, 0);
                            var cache = new byte[length];
                            if (workSocket.receiveAll(ref cache))
                            {
                                var tmp = cache.UnserializeFromBin<comInfo>();
                                switch (tmp.cType)
                                {
                                    case comType.main:
                                        if (tmp.mainIP == 0)
                                        {
                                            p2pIP = (new IPEndPoint(remoteIP.Address, BitConverter.ToInt32((byte[])tmp.obj,0))).toLong() ;
                                            callEvent(fl.clientIn, new infoArg { endp = remoteIP, iType = infoType.rmt_recObj }, true);
                                        }
                                        mainIP = tmp.mainIP;
                                        break;
                                    case comType.p2p:
                                        if (fl.cusers.TryGetValue(tmp.mainIP,out var declient))
                                        {
                                            tmp.mainIP = declient.p2pIP;
                                            send(tmp);
                                        }
                                        else
                                        {
                                            tmp.mainIP = 0;
                                            send(tmp);
                                        }
                                        break;
                                    case comType.obj:
                                        if (tmp.id == Guid.Empty)
                                        {
                                            callEvent(fl.recData, new netArg(remoteIP, false, true, tmp.obj, tmp.usertoken), true);

                                        }
                                        else
                                        {
                                            if (fl.replyCache.TryGetValue(tmp.id, out var tpifo))
                                            {
                                                tpifo.obj = tmp.obj;
                                                tpifo.autoEvent.Set();
                                            }
                                            else
                                            {
                                                if (fl.recData != null)
                                                {
                                                    Task.Factory.StartNew(() =>
                                                    {
                                                        var jg = new netArg(remoteIP, false, true, tmp.obj, tmp.usertoken);
                                                        fl.recData(fl, jg);
                                                        if (tmp.id!=Guid.Empty&& jg.argReply != null)
                                                        {
                                                            tmp.obj = jg.argReply;
                                                            tmp.usertoken = jg.userToken;
                                                            send(tmp);

                                                        }
                                                    });
                                                }
                                            }
                                        }
                                        break;
                                    case comType.file:
                                        switch (tmp.fType)
                                        {
                                            case fileType.receive:
                                                send(tmp);
                                                break;
                                            case fileType.send:
                                                var realfile = fl.tempDirectory + Path.GetFileName(tmp.fileName);
                                                var tmpfile = realfile + ".tmp";
                                                if (File.Exists(tmpfile) || File.Exists(realfile))
                                                {
                                                    realfile = fl.tempDirectory + Path.GetFileNameWithoutExtension(tmp.fileName) + "_" + DateTime.Now.ToString("yyyyMMddHHmmss") + Path.GetExtension(tmp.fileName);
                                                    tmpfile = realfile + ".tmp";
                                                }
                                                if (tcpTools.genTempFile(tmpfile, tmp.fileSize))
                                                {
                                                    workSocket.Send(new byte[] { 1 });
                                                    long cd = 0;
                                                    using (var mf = MemoryMappedFile.CreateFromFile(tmpfile, FileMode.Open))
                                                    {
                                                        using (var mvs = mf.CreateViewAccessor())
                                                        {
                                                            byte[] filebuff = new byte[fl.txBuff];
                                                            long rest = 0;
                                                            while (cd < tmp.fileSize)
                                                            {
                                                                if (!workSocket.isAlive())
                                                                {
                                                                    break;
                                                                }
                                                                rest = tmp.fileSize - cd;
                                                                if (rest <= fl.txBuff)
                                                                {
                                                                    if (!workSocket.receiveAll(ref filebuff, (int)rest))
                                                                    {
                                                                        break;
                                                                    }
                                                                    mvs.WriteArray(cd, filebuff, 0, (int)rest);
                                                                    cd += rest;
                                                                }
                                                                else
                                                                {
                                                                    if (!workSocket.receiveAll(ref filebuff))
                                                                    {
                                                                        break;
                                                                    }
                                                                    mvs.WriteArray(cd, filebuff, 0, fl.txBuff);
                                                                    cd += fl.txBuff;
                                                                }
                                                            }

                                                        }
                                                    }
                                                    if (cd != tmp.fileSize)
                                                    {
                                                        Thread.Sleep(500);
                                                        File.Delete(tmpfile);
                                                        fl.recData?.Invoke(fl, new netArg(mainIP == 0 ? remoteIP : mainIP.toIP(), true, false, tmp.fileName, tmp.usertoken));
                                                    }
                                                    else
                                                    {
                                                        File.Move(tmpfile, realfile);
                                                        fl.recData?.Invoke(fl, new netArg(mainIP == 0 ? remoteIP : mainIP.toIP(), true, true, realfile, tmp.usertoken));
                                                    }
                                                }
                                                else
                                                {
                                                    workSocket.Send(new byte[] { 0 });
                                                }
                                                break;
                                            default:
                                                break;
                                        }

                                        break;
                                    default:
                                        break;
                                }

                            }
                        }
                        workSocket.ReceiveAsync(e);
                    }
                }
                catch (Exception ex)
                {
#if debug
                    throw;
#endif
                    close();
                    if (!ismanstop)
                    {
                        if (mainIP == 0)
                        {
                            callEvent(fl.clientOut, new infoArg { endp = remoteIP, iType = infoType.rmt_recObj,message=ex.Message });
                            callEvent(fl.errorRec, new infoArg { endp = remoteIP, iType = infoType.rmt_recObj ,message=ex.Message});
                        }
                        else
                        {
                            callEvent(fl.errorRec, new infoArg { endp = remoteIP, iType = infoType.rmt_recFile ,message=ex.Message});
                        }

                    }
                    
                }
            }
            public void close(bool ownstop=false)
            {
                try
                {
                    if (ownstop)
                    {
                        removewrite.EnterWriteLock();
                        ismanstop = ownstop;
                        removewrite.ExitWriteLock();
                    }

                    workSocket.safeClose();
                    if (fl.cusers != null)
                    {
                        if (fl.cusers.ContainsKey(this.remoteIP.toLong()))
                        {
                            remoteClient tmp;
                            fl.cusers.TryRemove(this.remoteIP.toLong(), out tmp);
                        }
                        var fileSkt= fl.cusers.FirstOrDefault(n => n.Value.mainIP == this.remoteIP.toLong());
                        if (fileSkt.Key!=0)
                        {
                            fileSkt.Value.close(ownstop);
                        }
                    }

                }
                catch (Exception)
                {
#if debug
                    throw;
#endif
                    return;
                }
            }

            public void send(comInfo tmpData,bool canwait=true)
            {
                try
                {
                    if (!workSocket.isAlive())
                    {
                        close();
                        if (!ismanstop && mainIP == 0)
                        {
                            callEvent(fl.clientOut, new infoArg { endp = remoteIP, iType = infoType.rmt_sendStart });
                        }
                    }
                    else if (isSending&&canwait)
                    {
                        cachesend.Enqueue(tmpData);
                    }
                    else
                    {
                        if (canwait)
                        {
                            isSending = true;
                        }
                        switch (tmpData.cType)
                        {
                            case comType.p2p:
                            case comType.obj:
                                sdarg.UserToken = tmpData.usertoken;
                                var buffer = tmpData.SerializeToBin();
                                var tpack = new SendPacketsElement[2];
                                tpack[0] = new SendPacketsElement(BitConverter.GetBytes(buffer.LongLength));
                                tpack[1] = new SendPacketsElement(buffer);
                                sdarg.SendPacketsElements = tpack;
                                workSocket.SendPacketsAsync(sdarg);
                                break;
                            case comType.file:
                                if (!File.Exists(tmpData.fileName))
                                {
                                    workSocket.Send(BitConverter.GetBytes(-1L));
                                }
                                else
                                {
                                    FileInfo ifo = new FileInfo(tmpData.fileName);
                                    workSocket.Send(BitConverter.GetBytes(ifo.Length));
                                    using (var mf = MemoryMappedFile.CreateFromFile(tmpData.fileName, FileMode.Open))
                                    {
                                        using (var mvs = mf.CreateViewAccessor())
                                        {
                                            long cd = 0;

                                            byte[] filebuff = new byte[fl.txBuff];
                                            long rest = 0;
                                            while (cd < ifo.Length)
                                            {
                                                if (!workSocket.isAlive())
                                                {
                                                    break;
                                                }
                                                rest = ifo.Length - cd;
                                                if (rest <= fl.txBuff)
                                                {
                                                    mvs.ReadArray(cd, filebuff, 0, (int)rest);
                                                    if (!workSocket.sendAll(filebuff, (int)rest))
                                                    {
                                                        break;
                                                    }
                                                    cd += rest;
                                                }
                                                else
                                                {
                                                    mvs.ReadArray(cd, filebuff, 0, fl.txBuff);
                                                    if (!workSocket.sendAll(filebuff))
                                                    {
                                                        break;
                                                    }
                                                    cd += fl.txBuff;
                                                }
                                                if (fl.fileDelay > 0)
                                                {
                                                    Thread.Sleep(fl.fileDelay);
                                                }

                                            }
                                        }
                                    }



                                }
                                break;
                            default:
                                break;
                        }

                    }
                    
                }
                catch (Exception ex)
                {
#if debug
                    throw;
#endif
                    close();
                    if (!ismanstop)
                    {
                        if (mainIP == 0)
                        {
                            callEvent(fl.clientOut, new infoArg { endp = remoteIP, iType = infoType.rmt_sendObj, message = ex.Message });
                            callEvent(fl.errorRec, new infoArg { endp = remoteIP, iType = infoType.rmt_sendObj, message = ex.Message });
                        }
                        else
                        {
                            callEvent(fl.errorRec, new infoArg { endp = remoteIP, iType = infoType.rmt_sendObj, message = ex.Message });
                        }

                    }

                }
            }

            private void Sdarg_Completed(object sender, SocketAsyncEventArgs e)
            {
                try
                {
                    if (e.SocketError==SocketError.Success)
                    {
                        callEvent(fl.sendOver, new netArg(remoteIP, false, true, null, (int)e.UserToken), true);
                    }
                    else
                    {
                        callEvent(fl.sendOver, new netArg(remoteIP, false, false, null, (int)e.UserToken), true);
                    }
                    if (cachesend.Count > 0)
                    {
                        if (cachesend.TryDequeue(out var tmp))
                        {
                            send(tmp,false);
                        }
                        else
                        {
                            isSending = false;
                        }
                    }
                    else
                    {
                        isSending = false;

                    }
                }
                catch (Exception ex)
                {
#if debug
                    throw;
#endif
                    close();
                    if (!ismanstop)
                    {
                        callEvent(fl.clientOut, new infoArg { endp = remoteIP, iType = infoType.rmt_sendComplete, message = ex.Message });
                        callEvent(fl.errorRec, new infoArg { endp = remoteIP, iType = infoType.rmt_sendComplete, message = ex.Message });
                    }
                }
            }


        }
        private Socket server = null;
        private ConcurrentDictionary<long, remoteClient> cusers = new ConcurrentDictionary<long, remoteClient>();
        public event EventHandler<infoArg> clientIn;
        public event EventHandler<infoArg> clientOut;
        public event EventHandler<netArg> recData;
        public event EventHandler<netArg> sendOver;
        public event EventHandler<infoArg> unexpectedStop;
        public event EventHandler<infoArg> errorRec;
        private ConcurrentDictionary<Guid, replyIfo> replyCache;
        private int serverPort = 0;
        private bool ownClose = false;
        private bool isrun = false;
        private ReaderWriterLockSlim runlock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
        public int txBuff { get; private set; }
        public string tempDirectory { get; set; }
        public bool available
        {
            get
            {
                if (server != null && isrun)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }

        }
        public int fileDelay { get; set; }
        public tcpAsyncServer(int insevport, int sendFileDelay=0,int fileCache = 4*1024*1024)
        {
            serverPort = insevport;
            txBuff = fileCache;
            fileDelay = sendFileDelay;
            tempDirectory = AppDomain.CurrentDomain.BaseDirectory+"downTMP\\";
            if (!Directory.Exists(tempDirectory))
            {
                Directory.CreateDirectory(tempDirectory);
            }
        }
        private void callEvent<T>(EventHandler<T> inevt, T inarg, bool intask = false) where T : EventArgs
        {
            try
            {
                if (intask)
                {
                    if (inevt != null)
                    {
                        Task.Factory.StartNew(() =>
                        {
                            inevt(this, inarg);
                        });
                    }

                }
                else
                {
                    inevt?.Invoke(this, inarg);
                }
            }
            catch
            {
#if debug
                throw;
#endif
                return;
            }
        }

        public void start()
        {
            try
            {
                if (server != null && isrun)
                {
                    return;
                }
                replyCache = new ConcurrentDictionary<Guid, replyIfo>();
                ownClose = false;
                server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                server.Bind(new IPEndPoint(IPAddress.Any, serverPort));
                server.Listen(10);
                var acptArg = new SocketAsyncEventArgs();
                acptArg.Completed += acptArg_Completed;
                server.AcceptAsync(acptArg);
                runlock.EnterWriteLock();
                isrun = true;
                runlock.ExitWriteLock();
            }
            catch(Exception ex)
            {
#if debug
                    throw;
#endif
                runlock.EnterWriteLock();
                isrun = false;
                runlock.ExitWriteLock();
                callEvent(unexpectedStop, new infoArg { iType = infoType.srv_serverStart, message = ex.Message });
                callEvent(errorRec, new infoArg { iType = infoType.srv_serverStart, message = ex.Message });
            }


        }
        void acptArg_Completed(object sender, SocketAsyncEventArgs e)
        {

            try
            {
                var tpskt = e.AcceptSocket;
                e.AcceptSocket = null;
                server.AcceptAsync(e);
                if (tpskt!=null&&tpskt.RemoteEndPoint!=null)
                {

                    cusers.TryAdd((tpskt.RemoteEndPoint as IPEndPoint).toLong(), new remoteClient(tpskt,this));

                }
                else if (tpskt!=null && tpskt.RemoteEndPoint==null)
                {
                    tpskt.safeClose();
                }


            }
            catch (Exception ex)
            {
#if debug
                throw;
#endif

                shut();
                runlock.EnterWriteLock();
                isrun = false;
                runlock.ExitWriteLock();
                
                if (!ownClose)
                {
                    callEvent(unexpectedStop, new infoArg { iType = infoType.srv_accept, message = ex.Message });
                    callEvent(errorRec, new infoArg { iType = infoType.srv_accept, message = ex.Message });
                }
            }
        }
        public T getReply<T>(IPEndPoint deip, object inObj, int inusertoken = -1, int timeout=60000)
        {
            try
            {
                if (isrun)
                {
                    if (cusers.TryGetValue(deip.toLong(), out var sdsk))
                    {
                        if (timeout <= 0)
                        {
                            timeout = 1;
                        }
                        var objid = Guid.NewGuid();
                        var tifo = new replyIfo();
                        if (replyCache.TryAdd(objid, tifo))
                        {
                            var fsdata = new comInfo { cType=comType.obj, obj = inObj, id = objid, usertoken = inusertoken };

                            sdsk.send(fsdata);
                            if (tifo.autoEvent.WaitOne(timeout))
                            {
                                var ret = (T)tifo.obj;

                                replyCache.TryRemove(objid, out var tmp);
                                return ret;
                            }
                            else
                            {
                                replyCache.TryRemove(objid, out var tmp);
                                return default(T);
                            }
                        }
                        else
                        {
                            var fsdata = new comInfo { cType = comType.obj, obj = inObj, id = Guid.Empty, usertoken = inusertoken };

                            sdsk.send(fsdata);
                            return default(T);
                        }

                    }
                    else
                    {
                        return default(T);
                    }
                }
                else
                {
                    return default(T);
                }



            }
            catch(Exception ex)
            {

#if debug
                throw;
#endif
                shut();
                runlock.EnterWriteLock();
                isrun = false;
                runlock.ExitWriteLock();
                if (!ownClose)
                {
                    callEvent(unexpectedStop, new infoArg { iType = infoType.srv_getReply, message = ex.Message });
                    callEvent(errorRec, new infoArg { iType = infoType.srv_getReply, message = ex.Message });
                }
                return default(T);
            }
        }
        public void sendObject(IPEndPoint deip,object inObj,int inusertoken = -1)
        {
            try
            {

                if (isrun)
                {
                    if (cusers.TryGetValue(deip.toLong(), out var sdsk))
                    {
                        var fsdata = new comInfo { cType = comType.obj, obj = inObj, id = Guid.Empty, usertoken = inusertoken };
                        sdsk.send(fsdata);
                    }
                }



            }
            catch(Exception ex)
            {

#if debug
                throw;
#endif
                shut();
                runlock.EnterWriteLock();
                isrun = false;
                runlock.ExitWriteLock();
                if (!ownClose)
                {
                    callEvent(unexpectedStop, new infoArg { iType = infoType.srv_sendObj, message = ex.Message });
                    callEvent(errorRec, new infoArg { iType = infoType.srv_sendObj, message = ex.Message });
                }
            }
        }
        public List<IPEndPoint> getAllClients()
        {
            try
            {
                return cusers.Where(n => n.Value.mainIP == 0).Select(m => m.Value.remoteIP).ToList();
            }
            catch(Exception ex)
            {
#if debug
                throw;
#endif

                if (!ownClose)
                {
                    callEvent(errorRec, new infoArg { iType = infoType.srv_getAllClients, message = ex.Message });
                }
                return new List<IPEndPoint>();
            }

        }
        private void shut()
        {
            try
            {
                foreach (var item in cusers.Values)
                {
                    item.close(true);
                }
                cusers.Clear();
                server.safeClose();
            }
            catch (Exception ex)
            {
#if debug
                throw;
#endif

                cusers = new ConcurrentDictionary<long, remoteClient>();
                if (server!=null)
                {
                    server.safeClose();
                }
                callEvent(errorRec, new infoArg { iType = infoType.srv_shut, message = ex.Message });
            }

        }
        public void stop()
        {
            ownClose = true;
            runlock.EnterWriteLock();
            isrun = false;
            runlock.ExitWriteLock();
            shut();
        }
        public void closeClient(IPEndPoint deIP)
        {
            try
            {
                if (isrun)
                {
                    if (cusers.TryRemove(deIP.toLong(), out var sdsk))
                    {
                        sdsk.close(true);
                    }
                }


            }
            catch (Exception ex)
            {
#if debug
                throw;
#endif
                if (cusers.TryRemove(deIP.toLong(), out var sdsk))
                {
                    sdsk.close(true);
                }
                callEvent(errorRec, new infoArg { iType = infoType.srv_closeClient, message = ex.Message });
            }


        }
        public void closeAllClients()
        {

            try
            {
                if (isrun)
                {
                    foreach (var item in cusers.Values)
                    {
                        item.close(true);
                    }
                    cusers.Clear();
                }

            }
            catch (Exception ex)
            {
#if debug
                throw;
#endif
                shut();
                runlock.EnterWriteLock();
                isrun = false;
                runlock.ExitWriteLock();
                if (!ownClose)
                {
                    callEvent(unexpectedStop, new infoArg { iType = infoType.srv_closeAllClients, message = ex.Message });
                    callEvent(errorRec, new infoArg { iType = infoType.srv_closeAllClients, message = ex.Message });
                }
            }
        }

    }
    public class tcpAsyncClient
    {
        private Socket workSocket = null;
        private Socket p2pSocket = null;
        private ConcurrentDictionary<long, Socket> sktLst = new ConcurrentDictionary<long, Socket>();
        public int txBuff { get;private set; }
        public event EventHandler<netArg> connectResult;
        public event EventHandler<infoArg> disConnected;
        public event EventHandler<netArg> recData;
        public event EventHandler<fileArg> recFileRate;
        public event EventHandler<fileArg> sendFileRate;
        public event EventHandler<fileArg> recFileSpeed;
        public event EventHandler<fileArg> sendFileSpeed;
        public event EventHandler<netArg> sendOver;
        public event EventHandler<infoArg> errorRec;
        public IPEndPoint lcEDP { get; private set; } = null;
        private ReaderWriterLockSlim iplock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
        private bool ownClose = false;
        private ConcurrentQueue<comInfo> cachesend = new ConcurrentQueue<comInfo>();
        private ConcurrentDictionary<Guid, replyIfo> replyCache;
        private bool isSending = false;
        private SocketAsyncEventArgs recarg;
        private SocketAsyncEventArgs sdarg;
        private SocketAsyncEventArgs cntarg;
        private IPEndPoint srvhost;
        public string tempDirectory { get; set; }
        public bool iscnting { get; private set; } = false;
        public bool available
        {
            get
            {
                if (workSocket!=null && lcEDP!=null&&workSocket.Connected&&workSocket.isAlive())
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }

        }
        public int fileDelay { get; set; }
        public tcpAsyncClient(int sendFileDelay=0,int fileCache = 4 * 1024 * 1024)
        {
            fileDelay = sendFileDelay;
            txBuff = fileCache;
            tempDirectory = AppDomain.CurrentDomain.BaseDirectory + "downTMP\\";
            if (!Directory.Exists(tempDirectory))
            {
                Directory.CreateDirectory(tempDirectory);
            }
            cntarg = new SocketAsyncEventArgs();
            cntarg.Completed += Cntarg_Completed;
            recarg = new SocketAsyncEventArgs();
            recarg.SetBuffer(new byte[8], 0, 8);
            recarg.Completed += Recarg_Completed;
            sdarg = new SocketAsyncEventArgs();
            sdarg.Completed += Sdarg_Completed;
        }
        private void callEvent<T>(EventHandler<T> inevt, T inarg, bool intask = false) where T : EventArgs
        {
            try
            {
                if (intask)
                {
                    if (inevt != null)
                    {
                        Task.Factory.StartNew(() =>
                        {
                            inevt(this, inarg);
                        });
                    }

                }
                else
                {
                    inevt?.Invoke(this, inarg);
                }
            }
            catch
            {
#if debug
                throw;
#endif
                return;
            }
        }
        public bool connect(IPEndPoint deip)
        {
            if (available || iscnting)
            {
                return false;
            }
            iscnting = true;
            try
            {
                replyCache = new ConcurrentDictionary<Guid, replyIfo>();
                sktLst = new ConcurrentDictionary<long, Socket>();
                srvhost = deip;
                if (p2pSocket!=null)
                {
                    p2pSocket.safeClose();
                }
                p2pSocket= new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                p2pSocket.Bind(new IPEndPoint(IPAddress.Any, 0));
                p2pSocket.Listen(10);
                var p2pAcptArg= new SocketAsyncEventArgs();
                p2pAcptArg.Completed += P2pAcptArg_Completed;
                p2pSocket.AcceptAsync(p2pAcptArg);
                workSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                workSocket.Connect(deip);
                if (workSocket.isAlive())
                {
                    iplock.EnterWriteLock();
                    lcEDP = workSocket.LocalEndPoint as IPEndPoint;
                    iplock.ExitWriteLock();
                    workSocket.IOControl(IOControlCode.KeepAliveValues, tcpTools.keepAliveValues(1, 1000, 1000), null);
                    var buffer = (new comInfo { cType = comType.main,obj=BitConverter.GetBytes((p2pSocket.LocalEndPoint as IPEndPoint).Port) }).SerializeToBin();
                    var tp = buffer.UnserializeFromBin<comInfo>();
                    workSocket.sendAllPack(buffer);
                    workSocket.ReceiveAsync(recarg);
                    return true;
                }
                else
                {
                    close();
                    return false;
                }
            }
            catch (Exception ex)
            {
#if debug
                throw;
#endif
                close();
                callEvent(errorRec, new infoArg { iType = infoType.cli_connect, message = ex.Message });
                return false;
            }
            finally
            {
                iscnting = false;
            }
           

        }

        public void connectAsync(IPEndPoint deip)
        {
            try
            {
                if (available || iscnting)
                {
                    return;
                }
                iscnting = true;
                iplock.EnterWriteLock();
                lcEDP = null;
                iplock.ExitWriteLock();
                ownClose = false;
                replyCache = new ConcurrentDictionary<Guid, replyIfo>();
                srvhost = deip;
                if (p2pSocket!=null)
                {
                    p2pSocket.safeClose();
                }
                p2pSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                p2pSocket.Bind(new IPEndPoint(IPAddress.Any, 0));
                p2pSocket.Listen(10);
                var p2pAcptArg = new SocketAsyncEventArgs();
                p2pAcptArg.Completed += P2pAcptArg_Completed;
                p2pSocket.AcceptAsync(p2pAcptArg);
                cntarg.RemoteEndPoint = srvhost;
                workSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                workSocket.ConnectAsync(cntarg);
            }
            catch(Exception ex)
            {
#if debug
                    throw;
#endif
                close();
                callEvent(connectResult, new netArg(srvhost,false,false,null));
                callEvent(errorRec, new infoArg { iType = infoType.cli_connectAsync, message = ex.Message });
                iscnting = false;
                
            }

        }
        private void P2pAcptArg_Completed(object sender, SocketAsyncEventArgs e)
        {
            var tpskt = e.AcceptSocket;
            try
            {
                
                e.AcceptSocket = null;
                p2pSocket.AcceptAsync(e);
                if (tpskt != null && tpskt.RemoteEndPoint != null)
                {
                    sktLst.TryAdd(((IPEndPoint)tpskt.RemoteEndPoint).toLong(), tpskt);
                    tpskt.IOControl(IOControlCode.KeepAliveValues, tcpTools.keepAliveValues(1, 1000, 1000), null);
                    var p2precarg = new SocketAsyncEventArgs();
                    p2precarg.SetBuffer(new byte[8], 0, 8);
                    p2precarg.Completed += P2precarg_Completed;
                    tpskt.ReceiveAsync(p2precarg);
                }

            }
            catch (Exception ex)
            {

                sktLst.TryRemove(((IPEndPoint)tpskt.RemoteEndPoint).toLong(), out var tp);
                if (tpskt != null)
                {
                    tpskt.safeClose();
                }
                if (p2pSocket != null)
                {
                    p2pSocket.safeClose();
                }
                callEvent(errorRec, new infoArg { iType = infoType.cli_p2pAccept, message = ex.Message });

            }
        }

        private void P2precarg_Completed(object sender, SocketAsyncEventArgs e)
        {
            var tpskt = (Socket)sender;
            try
            {

                if (tpskt.isAlive())
                {
                    if (e.SocketError == SocketError.Success && e.BytesTransferred == 8)
                    {
                        var length = BitConverter.ToInt64(e.Buffer, 0);
                        var cache = new byte[length];
                        if (tpskt.receiveAll(ref cache))
                        {
                            var tmp = cache.UnserializeFromBin<comInfo>();
                            var rmtIP = tmp.mainIP.toIP();
                            switch (tmp.cType)
                            {

                                case comType.obj:
                                    var jg = new netArg(rmtIP, false, true, tmp.obj, tmp.usertoken);
                                    callEvent(recData, jg);
                                    if (tmp.id != Guid.Empty && jg.argReply != null)
                                    {
                                        tmp.obj = jg.argReply;
                                        tmp.usertoken = jg.userToken;
                                        tpskt.sendAllPack(tmp.SerializeToBin());
                                    }

                                    break;
                                case comType.file:
                                    var realfile = tempDirectory + Path.GetFileName(tmp.fileName);
                                    var tmpfile = realfile + ".tmp";
                                    if (File.Exists(tmpfile) || File.Exists(realfile))
                                    {
                                        realfile = tempDirectory + Path.GetFileNameWithoutExtension(tmp.fileName) + "_" + DateTime.Now.ToString("yyyyMMddHHmmss") + Path.GetExtension(tmp.fileName);
                                        tmpfile = realfile + ".tmp";
                                    }
                                    if (tcpTools.genTempFile(tmpfile, tmp.fileSize))
                                    {
                                        tpskt.Send(new byte[] { 1 });
                                        long cd = 0;
                                        using (var mf = MemoryMappedFile.CreateFromFile(tmpfile, FileMode.Open))
                                        {
                                            using (var mvs = mf.CreateViewAccessor())
                                            {
                                                byte[] filebuff = new byte[txBuff];
                                                Stopwatch stp = new Stopwatch();
                                                long spd = 0;
                                                long rest = 0;
                                                stp.Start();
                                                while (cd < tmp.fileSize)
                                                {
                                                    if (!tpskt.isAlive())
                                                    {
                                                        break;
                                                    }
                                                    rest = tmp.fileSize - cd;

                                                    if (rest <= txBuff)
                                                    {
                                                        if (!tpskt.receiveAll(ref filebuff, (int)rest))
                                                        {
                                                            break;
                                                        }
                                                        mvs.WriteArray(cd, filebuff, 0, (int)rest);
                                                        cd += rest;
                                                    }
                                                    else
                                                    {
                                                        if (!tpskt.receiveAll(ref filebuff))
                                                        {
                                                            break;
                                                        }
                                                        mvs.WriteArray(cd, filebuff, 0, txBuff);
                                                        cd += txBuff;
                                                    }
                                                    if (recFileRate != null)
                                                    {
                                                        var dt = cd;
                                                        Task.Factory.StartNew(() =>
                                                        {
                                                            var rate = dt / (double)tmp.fileSize;
                                                            recFileRate(this, new fileArg { endp = rmtIP, fileName = realfile, fileState = fileType.receive, str = rate.ToString("p2"), value = rate, userToken = tmp.usertoken });
                                                        });
                                                    }

                                                    if (recFileSpeed != null)
                                                    {
                                                        if (spd == 0 || (spd > 0 && stp.Elapsed.TotalSeconds >= 1))
                                                        {
                                                            var dt = cd - spd;
                                                            spd = cd;
                                                            var sj = stp.Elapsed.TotalSeconds;
                                                            Task.Factory.StartNew(() =>
                                                            {
                                                                var speed = dt / sj;
                                                                recFileSpeed(this, new fileArg { endp = rmtIP, fileName = realfile, fileState = fileType.receive, str = speed.fileSize() + "/s", value = speed, userToken = tmp.usertoken });
                                                            });
                                                            stp.Restart();
                                                        }

                                                    }
                                                }
                                                stp.Stop();

                                            }
                                        }
                                        if (cd != tmp.fileSize)
                                        {
                                            Thread.Sleep(500);
                                            File.Delete(tmpfile);
                                            recData?.Invoke(this, new netArg(rmtIP, true, false, tmp.fileName, tmp.usertoken));
                                        }
                                        else
                                        {
                                            File.Move(tmpfile, realfile);
                                            recData?.Invoke(this, new netArg(rmtIP, true, true, realfile, tmp.usertoken));
                                        }

                                    }
                                    else
                                    {
                                        tpskt.Send(new byte[] { 0 });
                                        recData?.Invoke(this, new netArg(rmtIP, true, false, tmp.fileName, tmp.usertoken));
                                    }

                                    break;
                                default:
                                    break;
                            }

                        }
                    }
                }
            }
            catch (Exception ex)
            {
                if (p2pSocket != null)
                {
                    p2pSocket.safeClose();
                }
                callEvent(errorRec, new infoArg { iType = infoType.cli_p2pRec, message = ex.Message });

            }
            finally
            {
                sktLst.TryRemove(((IPEndPoint)tpskt.RemoteEndPoint).toLong(), out var tp);
                if (tpskt != null)
                {
                    tpskt.safeClose();
                }
                

            }
        }

        private void Cntarg_Completed(object sender, SocketAsyncEventArgs e)
        {
            try
            {
                if (e.ConnectSocket != null)
                {
                    iplock.EnterWriteLock();
                    lcEDP = workSocket.LocalEndPoint as IPEndPoint;
                    iplock.ExitWriteLock();
                    workSocket.IOControl(IOControlCode.KeepAliveValues, tcpTools.keepAliveValues(1, 1000, 1000), null);
                    var buffer = (new comInfo { cType = comType.main,obj= BitConverter.GetBytes((p2pSocket.LocalEndPoint as IPEndPoint).Port) }).SerializeToBin();
                    workSocket.sendAllPack(buffer);
                    callEvent(connectResult, new netArg(srvhost, false, true, null), true);
                    workSocket.ReceiveAsync(recarg);
                }
                else
                {
                    
                    close();
                    callEvent(connectResult, new netArg(srvhost, false, false, null));
                }
            }
            catch (Exception ex)
            {
#if debug
                throw;
#endif
                close();
                
                if (!ownClose)
                {
                    callEvent(connectResult, new netArg(srvhost, false, false, null));
                    callEvent(errorRec, new infoArg { iType = infoType.cli_connectComplete, message = ex.Message });
                }
                
            }
            finally
            {
                iscnting = false;
            }

        }

        private void Recarg_Completed(object sender, SocketAsyncEventArgs e)
        {

            try
            {
                if (!workSocket.isAlive())
                {
                    close();
                    
                    if (!ownClose)
                    {
                        callEvent(disConnected, new infoArg { endp = srvhost, iType = infoType.cli_recComplete });
                    }
                }
                else
                {
                    if (e.SocketError == SocketError.Success && e.BytesTransferred == 8)
                    {
                        var length = BitConverter.ToInt64(e.Buffer, 0);
                        var cache = new byte[length];
                        if (workSocket.receiveAll(ref cache))
                        {
                            var tmp = cache.UnserializeFromBin<comInfo>();

                            if (tmp.id == Guid.Empty)
                            {
                                callEvent(recData, new netArg(srvhost, false, true, tmp.obj, tmp.usertoken), true);
                            }
                            else
                            {
                                if (replyCache.TryGetValue(tmp.id, out var tpifo))
                                {
                                    if (tmp.cType==comType.p2p)
                                    {
                                        if (tmp.mainIP!=0)
                                        {
                                            tpifo.obj = tmp.mainIP.toIP();
                                        }
                                        else
                                        {
                                            tpifo.obj = null;
                                        }
                                        
                                    }
                                    else
                                    {
                                        tpifo.obj = tmp.obj;
                                    }
                                    
                                    tpifo.autoEvent.Set();
                                }
                                else
                                {
                                    if (recData != null)
                                    {
                                        Task.Factory.StartNew(() =>
                                        {
                                            var jg = new netArg(srvhost, false, true, tmp.obj, tmp.usertoken);
                                            recData(this, jg);
                                            if (tmp.id!=Guid.Empty&& jg.argReply != null)
                                            {
                                                tmp.obj = jg.argReply;
                                                tmp.usertoken = jg.userToken;
                                                send(tmp);

                                            }
                                        });
                                    }
                                }
                            }

                        }
                    }
                    workSocket.ReceiveAsync(e);
                }
            }
            catch(Exception ex)
            {
#if debug
                throw;
#endif
                close();
                if (!ownClose)
                {
                    callEvent(disConnected, new infoArg { iType = infoType.cli_recObj, message = ex.Message });
                    callEvent(errorRec, new infoArg { iType = infoType.cli_recObj, message = ex.Message });
                }

            }
        }
        public void sendFile(string filename,int inusertoken=-1)
        {
            if (available && File.Exists(filename))
            {
                Task.Factory.StartNew(()=> {
                    Socket fileSocket = null;
                    try
                    {

                        fileSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                        fileSocket.Connect(srvhost);
                        if (fileSocket.isAlive())
                        {
                            sktLst.TryAdd(((IPEndPoint)fileSocket.LocalEndPoint).toLong(), fileSocket);
                            fileSocket.IOControl(IOControlCode.KeepAliveValues, tcpTools.keepAliveValues(1, 1000, 1000), null);
                            var buffer = (new comInfo { cType = comType.main, mainIP = lcEDP.toLong() }).SerializeToBin();
                            fileSocket.sendAllPack(buffer);

                            var ifo = new FileInfo(filename);
                            var tpIfo = new comInfo { fileName = filename, fileSize = ifo.Length, fType = fileType.send, cType = comType.file, usertoken = inusertoken };
                            fileSocket.sendAllPack(tpIfo.SerializeToBin());
                            var sdbf = new byte[1];
                            if (fileSocket.receiveAll(ref sdbf))
                            {
                                if (sdbf[0] == 1)
                                {
                                    using (var mf = MemoryMappedFile.CreateFromFile(filename, FileMode.Open))
                                    {
                                        using (var mvs = mf.CreateViewAccessor())
                                        {
                                            long cd = 0;
                                            long rest = 0;
                                            Stopwatch stp = new Stopwatch();
                                            long spd = 0;
                                            byte[] filebuff = new byte[txBuff];
                                            stp.Start();
                                            while (cd < ifo.Length)
                                            {
                                                if (!fileSocket.isAlive())
                                                {
                                                    break;
                                                }
                                                rest = ifo.Length - cd;
                                                if (rest <= txBuff)
                                                {

                                                    mvs.ReadArray(cd, filebuff, 0, (int)rest);
                                                    if (!fileSocket.sendAll(filebuff, (int)rest))
                                                    {
                                                        break;
                                                    }
                                                    cd += rest;
                                                }
                                                else
                                                {
                                                    mvs.ReadArray(cd, filebuff, 0, txBuff);
                                                    if (!fileSocket.sendAll(filebuff))
                                                    {
                                                        break;
                                                    }
                                                    cd += txBuff;
                                                }
                                                if (sendFileRate != null)
                                                {
                                                    var dt = cd;
                                                    Task.Factory.StartNew(() =>
                                                    {
                                                        var rate = dt / (double)ifo.Length;
                                                        sendFileRate(this, new fileArg { endp = srvhost, fileName = filename, fileState = fileType.send, str = rate.ToString("p2"), value = rate, userToken = inusertoken });
                                                    });
                                                }
                                                if (sendFileSpeed != null)
                                                {
                                                    if (spd == 0 || (spd > 0 && stp.Elapsed.TotalSeconds >= 1))
                                                    {
                                                        var dt = cd - spd;
                                                        spd = cd;
                                                        var sj = stp.Elapsed.TotalSeconds;
                                                        Task.Factory.StartNew(() =>
                                                        {
                                                            var speed = dt / sj;
                                                            sendFileSpeed(this, new fileArg { endp = srvhost, fileName = filename, fileState = fileType.send, str = speed.fileSize() + "/s", value = speed, userToken = inusertoken });
                                                        });
                                                        stp.Restart();
                                                    }
                                                }
                                                if (fileDelay > 0)
                                                {
                                                    Thread.Sleep(fileDelay);
                                                }

                                            }
                                            stp.Stop();
                                            if (cd != ifo.Length)
                                            {
                                                callEvent(sendOver, new netArg(srvhost, true, false, filename, inusertoken));
                                            }
                                            else
                                            {
                                                callEvent(sendOver, new netArg(srvhost, true, true, filename, inusertoken));
                                            }
                                        }
                                    }


                                }
                                else
                                {
                                    callEvent(sendOver, new netArg(srvhost, true, false, filename, inusertoken));
                                }
                            }
                            else
                            {
                                callEvent(sendOver, new netArg(srvhost, true, false, filename, inusertoken));
                            }
                        }
                        else
                        {
                            callEvent(sendOver, new netArg(srvhost, true, false, filename, inusertoken));
                        }

                    }
                    catch (Exception ex)
                    {
                        callEvent(sendOver, new netArg(srvhost, true, false, filename, inusertoken));
                        if (!ownClose)
                        {
                            callEvent(errorRec, new infoArg { iType = infoType.cli_sendfile, message = ex.Message });
                        }
                    }
                    finally
                    {
                        if (fileSocket != null)
                        {
                            sktLst.TryRemove(((IPEndPoint)fileSocket.LocalEndPoint).toLong(), out var tp);
                            fileSocket.safeClose();
                        }
                    }


                });

                
            }
        }
        public void getFile(string filename,int inusertoken = -1, string localName=null)
        {
            if (available)
            {
                Task.Factory.StartNew(() =>
                {
                    Socket fileSocket = null;
                    try
                    {
                        fileSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                        fileSocket.Connect(srvhost);
                        if (fileSocket.isAlive())
                        {
                            sktLst.TryAdd(((IPEndPoint)fileSocket.LocalEndPoint).toLong(), fileSocket);
                            fileSocket.IOControl(IOControlCode.KeepAliveValues, tcpTools.keepAliveValues(1, 1000, 1000), null);
                            var buffer = (new comInfo { cType = comType.main, mainIP = lcEDP.toLong() }).SerializeToBin();
                            fileSocket.sendAllPack(buffer);
                            var tpIfo = new comInfo { fileName = filename, fType = fileType.receive, cType = comType.file, usertoken = inusertoken };
                            fileSocket.sendAllPack(tpIfo.SerializeToBin());
                            var lenbf = new byte[8];
                            if (fileSocket.receiveAll(ref lenbf))
                            {
                                var len = BitConverter.ToInt64(lenbf, 0);
                                if (len < 0)
                                {
                                    callEvent(recData, new netArg(srvhost, true, false, filename, inusertoken));
                                }
                                else
                                {
                                    if (string.IsNullOrWhiteSpace(localName))
                                    {
                                        localName = tempDirectory + Path.GetFileName(filename);
                                        if (File.Exists(localName))
                                        {
                                            localName = tempDirectory + Path.GetFileNameWithoutExtension(filename) + "_" + DateTime.Now.ToString("yyyyMMddHHmmss") + Path.GetExtension(filename);
                                        }
                                    }
                                    var realfile = localName;
                                    var tmpfile = realfile + ".tmp";
                                    long cd = 0;
                                    if (tcpTools.genTempFile(tmpfile, len))
                                    {
                                        using (var mf = MemoryMappedFile.CreateFromFile(tmpfile, FileMode.Open))
                                        {
                                            using (var mvs = mf.CreateViewAccessor())
                                            {
                                                byte[] filebuff = new byte[txBuff];
                                                Stopwatch stp = new Stopwatch();
                                                long spd = 0;
                                                long rest = 0;
                                                stp.Start();
                                                while (cd < len)
                                                {
                                                    if (!fileSocket.isAlive())
                                                    {
                                                        break;
                                                    }
                                                    rest = len - cd;

                                                    if (rest <= txBuff)
                                                    {
                                                        if (!fileSocket.receiveAll(ref filebuff, (int)rest))
                                                        {
                                                            break;
                                                        }
                                                        mvs.WriteArray(cd, filebuff, 0, (int)rest);
                                                        cd += rest;
                                                    }
                                                    else
                                                    {
                                                        if (!fileSocket.receiveAll(ref filebuff))
                                                        {
                                                            break;
                                                        }
                                                        mvs.WriteArray(cd, filebuff, 0, txBuff);
                                                        cd += txBuff;
                                                    }
                                                    if (recFileRate != null)
                                                    {
                                                        var dt = cd;
                                                        Task.Factory.StartNew(() =>
                                                        {
                                                            var rate = dt / (double)len;
                                                            recFileRate(this, new fileArg { endp = srvhost, fileName = realfile, fileState = fileType.receive, str = rate.ToString("p2"), value = rate, userToken = inusertoken });
                                                        });
                                                    }

                                                    if (recFileSpeed != null)
                                                    {
                                                        if (spd == 0 || (spd > 0 && stp.Elapsed.TotalSeconds >= 1))
                                                        {
                                                            var dt = cd - spd;
                                                            spd = cd;
                                                            var sj = stp.Elapsed.TotalSeconds;
                                                            Task.Factory.StartNew(() =>
                                                            {
                                                                var speed = dt / sj;
                                                                recFileSpeed(this, new fileArg { endp = srvhost, fileName = realfile, fileState = fileType.receive, str = speed.fileSize() + "/s", value = speed, userToken = inusertoken });
                                                            });
                                                            stp.Restart();
                                                        }

                                                    }
                                                }
                                                stp.Stop();

                                            }
                                        }
                                        Task.Factory.StartNew(() =>
                                        {
                                            if (cd != len)
                                            {
                                                Thread.Sleep(500);
                                                File.Delete(tmpfile);
                                                recData?.Invoke(this, new netArg(srvhost, true, false, filename, inusertoken));
                                            }
                                            else
                                            {
                                                File.Move(tmpfile, realfile);
                                                recData?.Invoke(this, new netArg(srvhost, true, true, realfile, inusertoken));
                                            }
                                        });
                                    }
                                    else
                                    {
                                        callEvent(recData, new netArg(srvhost, true, false, filename, inusertoken));
                                    }



                                }
                            }
                            else
                            {

                                callEvent(recData, new netArg(srvhost, true, false, filename, inusertoken));
                            }
                        }
                        else
                        {
                            callEvent(recData, new netArg(srvhost, true, false, filename, inusertoken));
                        }
                    }
                    catch (Exception ex)
                    {
                        callEvent(recData, new netArg(srvhost, true, false, filename, inusertoken));
                        if (!ownClose)
                        {
                            callEvent(errorRec, new infoArg { iType = infoType.cli_getfile, message = ex.Message });
                        }

                    }
                    finally
                    {
                        if (fileSocket != null)
                        {
                            sktLst.TryRemove(((IPEndPoint)fileSocket.LocalEndPoint).toLong(), out var tp);
                            fileSocket.safeClose();
                        }
                    }
                });
            }
            


        }
        private void send(comInfo tmpData,bool canwait=true)
        {

            try
            {
                if (!workSocket.isAlive())
                {
                    close();
                    if (!ownClose)
                    {
                        callEvent(disConnected, new infoArg { endp = srvhost, iType = infoType.cli_sendStart });
                    }
                }
                else if (isSending&&canwait)
                {
                    cachesend.Enqueue(tmpData);
                }
                else
                {
                    if (canwait)
                    {
                        isSending = true;
                    }

                    sdarg.UserToken = tmpData.usertoken;
                    var buffer = tmpData.SerializeToBin();
                    var tpack = new SendPacketsElement[2];
                    tpack[0] = new SendPacketsElement(BitConverter.GetBytes(buffer.LongLength));
                    tpack[1] = new SendPacketsElement(buffer);
                    sdarg.SendPacketsElements = tpack;
                    workSocket.SendPacketsAsync(sdarg);

                }

            }
            catch (Exception ex)
            {
#if debug
                    throw;
#endif
                close();

                if (!ownClose)
                {
                    callEvent(disConnected, new infoArg { iType = infoType.cli_sendObj, message = ex.Message });
                    callEvent(errorRec, new infoArg { iType = infoType.cli_sendObj, message = ex.Message });
                }
            }
        }

        private void Sdarg_Completed(object sender, SocketAsyncEventArgs e)
        {
            try
            {
                if (e.SocketError == SocketError.Success)
                {
                    callEvent(sendOver, new netArg(srvhost, false, true, null, (int)e.UserToken), true);
                }
                else
                {
                    callEvent(sendOver, new netArg(srvhost, false, false, null, (int)e.UserToken), true);
                }
                if (cachesend.Count > 0)
                {
                    if (cachesend.TryDequeue(out var tmp))
                    {
                        send(tmp,false);
                    }
                    else
                    {
                        isSending = false;
                    }
                }
                else
                {
                    isSending = false;

                }
            }
            catch (Exception ex)
            {
#if debug
                    throw;
#endif
                close();


                if (!ownClose)
                {
                    callEvent(disConnected, new infoArg { iType = infoType.cli_sendComplete, message = ex.Message });
                    callEvent(errorRec, new infoArg { iType = infoType.cli_sendComplete, message = ex.Message });
                }
            }
        }
        public T getReply<T>(object inObj, int inusertoken = -1, int timeout = 60000)
        {
            try
            {
                if (available)
                {
                    if (timeout <= 0)
                    {
                        timeout = 1;
                    }
                    var objid = Guid.NewGuid();
                    var tifo = new replyIfo();
                    if (replyCache.TryAdd(objid, tifo))
                    {
                        var fsdata = new comInfo { cType=comType.obj, obj = inObj, id = objid, usertoken = inusertoken };
                        send(fsdata);
                        if (tifo.autoEvent.WaitOne(timeout))
                        {
                            var ret = (T)tifo.obj;
                            replyCache.TryRemove(objid, out var tmp);
                            return ret;
                        }
                        else
                        {
                            replyCache.TryRemove(objid, out var tmp);
                            return default(T);
                        }

                    }
                    else
                    {
                        var fsdata = new comInfo { cType=comType.obj, obj = inObj, id = Guid.Empty, usertoken = inusertoken };
                        send(fsdata);
                        return default(T);
                    }
                }
                else
                {
                    return default(T);
                }
 

            }
            catch(Exception ex)
            {
#if debug
                throw;
#endif
                close();
                if (!ownClose)
                {
                    callEvent(disConnected, new infoArg { iType = infoType.cli_getReply, message = ex.Message });
                    callEvent(errorRec, new infoArg { iType = infoType.cli_getReply, message = ex.Message });

                }
                return default(T);
            }
        }
        public void sendObject(object inObj, int inusertoken = -1)
        {
            try
            {
                if (available)
                {
                    var fsdata = new comInfo { cType = comType.obj, obj = inObj, id = Guid.Empty, usertoken = inusertoken };
                    send(fsdata);
                }

            }
            catch(Exception ex)
            {
#if debug
                throw;
#endif
                close();
                if (!ownClose)
                {
                    callEvent(disConnected, new infoArg { iType = infoType.cli_sendPlan, message = ex.Message });
                    callEvent(errorRec, new infoArg { iType = infoType.cli_sendPlan, message = ex.Message });

                }
            }
        }
        private IPEndPoint getP2pSrv(IPEndPoint deIP)
        {
            try
            {
                if (available)
                {
                    int timeout = 60000;
                    var objid = Guid.NewGuid();
                    var tifo = new replyIfo();
                    if (replyCache.TryAdd(objid, tifo))
                    {
                        var fsdata = new comInfo { cType = comType.p2p, mainIP = deIP.toLong(), id = objid };
                        send(fsdata);
                        if (tifo.autoEvent.WaitOne(timeout))
                        {
                            if (tifo.obj==null)
                            {
                                replyCache.TryRemove(objid, out var tmp);
                                return null;
                            }
                            else
                            {
                                var ret = (IPEndPoint)tifo.obj;
                                replyCache.TryRemove(objid, out var tmp);
                                return ret;
                            }

                        }
                        else
                        {
                            replyCache.TryRemove(objid, out var tmp);
                            return null;
                        }

                    }
                    else
                    {
                        return null;
                    }
                }
                else
                {
                    return null;
                }
                

            }
            catch (Exception ex)
            {

#if debug
                throw;
#endif
                close();
                if (!ownClose)
                {
                    callEvent(disConnected, new infoArg { iType = infoType.cli_getP2pSrv, message = ex.Message });
                    callEvent(errorRec, new infoArg { iType = infoType.cli_getP2pSrv, message = ex.Message });

                }
                return null;
            }
        }
        public void sendObjectP2P(IPEndPoint deIP,object inObj,int inusertoken = -1)
        {
            if (available)
            {
                Task.Factory.StartNew(() =>
                {
                    var srvip = getP2pSrv(deIP);
                    Socket p2pclient = null;
                    try
                    {
                        if (srvip != null)
                        {
                            p2pclient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                            p2pclient.Connect(srvip);
                            if (p2pclient.isAlive())
                            {
                                sktLst.TryAdd(((IPEndPoint)p2pclient.RemoteEndPoint).toLong(), p2pclient);
                                p2pclient.IOControl(IOControlCode.KeepAliveValues, tcpTools.keepAliveValues(1, 1000, 1000), null);
                                var fsdata = new comInfo { cType = comType.obj, mainIP = lcEDP.toLong(), obj = inObj, id = Guid.Empty, usertoken = inusertoken };
                                if (p2pclient.sendAllPack(fsdata.SerializeToBin()))
                                {
                                    callEvent(sendOver, new netArg(deIP, false, true, null, inusertoken));
                                }
                                else
                                {
                                    callEvent(sendOver, new netArg(deIP, false, false, null, inusertoken));
                                }
                            }
                            else
                            {
                                callEvent(sendOver, new netArg(deIP, false, false, null, inusertoken));
                            }
                        }
                        else
                        {
                            callEvent(sendOver, new netArg(deIP, false, false, null, inusertoken));
                        }

                    }
                    catch (Exception ex)
                    {

                        callEvent(sendOver, new netArg(deIP, false, false, null, inusertoken));
                        if (!ownClose)
                        {
                            callEvent(errorRec, new infoArg { iType = infoType.cli_sendObjP2P, message = ex.Message });
                        }
                    }
                    finally
                    {
                        if (p2pclient != null)
                        {
                            sktLst.TryRemove(((IPEndPoint)p2pclient.LocalEndPoint).toLong(), out var tp);
                            p2pclient.safeClose();
                        }
                    }
                });

            }

        }
        public T getReplyP2P<T>(IPEndPoint deIP,object inObj, int inusertoken = -1, int timeout = 60000)
        {
            if (available)
            {
                var srvip = getP2pSrv(deIP);
                Socket p2pclient = null;
                try
                {
                    if (srvip != null)
                    {
                        if (timeout <= 0)
                        {
                            timeout = 1;
                        }
                        p2pclient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                        p2pclient.Connect(srvip);
                        if (p2pclient.isAlive())
                        {
                            sktLst.TryAdd(((IPEndPoint)p2pclient.RemoteEndPoint).toLong(), p2pclient);
                            p2pclient.IOControl(IOControlCode.KeepAliveValues, tcpTools.keepAliveValues(1, 1000, 1000), null);
                            var fsdata = new comInfo { cType = comType.obj, mainIP = lcEDP.toLong(), obj = inObj, id = Guid.NewGuid(), usertoken = inusertoken };
                            if (p2pclient.sendAllPack(fsdata.SerializeToBin()))
                            {
                                var aret = new AutoResetEvent(false);
                                comInfo jg = null;
                                var p2pArg = new SocketAsyncEventArgs();
                                p2pArg.SetBuffer(new byte[8], 0, 8);
                                p2pArg.Completed += (s, e) =>
                                {
                                    try
                                    {
                                        if (e.SocketError == SocketError.Success && e.BytesTransferred == 8)
                                        {
                                            var length = BitConverter.ToInt64(e.Buffer, 0);
                                            var buffer = new byte[length];
                                            if (p2pclient.receiveAll(ref buffer))
                                            {
                                                jg = buffer.UnserializeFromBin<comInfo>();
                                            }
                                        }
                                    }
                                    catch (Exception)
                                    {

                                        throw;
                                    }
                                    finally
                                    {
                                        aret.Set();
                                    }
                                };
                                p2pclient.ReceiveAsync(p2pArg);
                                if (aret.WaitOne(timeout))
                                {
                                    if (jg!=null)
                                    {
                                        return (T)jg.obj;
                                    }
                                    else
                                    {
                                        return default(T);
                                    }
                                }
                                else
                                {
                                    return default(T);
                                }
                            }
                            else
                            {
                                return default(T);
                            }
                        }
                        else
                        {
                            return default(T);
                        }

                    }
                    else
                    {
                        return default(T);
                    }

                }
                catch (Exception ex)
                {


                    if (!ownClose)
                    {
                        callEvent(errorRec, new infoArg { iType = infoType.cli_getReplyP2P, message = ex.Message });
                    }
                    return default(T);
                }
                finally
                {
                    if (p2pclient != null)
                    {
                        sktLst.TryRemove(((IPEndPoint)p2pclient.LocalEndPoint).toLong(), out var tp);
                        p2pclient.safeClose();
                    }
                }
            }
            else
            {
                return default(T);
            }

        }
        public void sendFileP2P(IPEndPoint deIP,string filename, int inusertoken = -1)
        {
            if (available&&File.Exists(filename))
            {
                var srvip = getP2pSrv(deIP);
                Socket p2pclient = null;
                try
                {
                    if (srvip!=null)
                    {
                        p2pclient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                        p2pclient.Connect(srvip);
                        if (p2pclient.isAlive())
                        {
                            sktLst.TryAdd(((IPEndPoint)p2pclient.RemoteEndPoint).toLong(), p2pclient);
                            p2pclient.IOControl(IOControlCode.KeepAliveValues, tcpTools.keepAliveValues(1, 1000, 1000), null);
                            var ifo = new FileInfo(filename);
                            var tpIfo = new comInfo { fileName = filename, fileSize = ifo.Length, mainIP=lcEDP.toLong(),fType = fileType.send, cType = comType.file, usertoken = inusertoken };
                            p2pclient.sendAllPack(tpIfo.SerializeToBin());
                            var sdbf = new byte[1];
                            if (p2pclient.receiveAll(ref sdbf))
                            {
                                if (sdbf[0] == 1)
                                {
                                    using (var mf = MemoryMappedFile.CreateFromFile(filename, FileMode.Open))
                                    {
                                        using (var mvs = mf.CreateViewAccessor())
                                        {
                                            long cd = 0;
                                            long rest = 0;
                                            Stopwatch stp = new Stopwatch();
                                            long spd = 0;
                                            byte[] filebuff = new byte[txBuff];
                                            stp.Start();
                                            while (cd < ifo.Length)
                                            {
                                                if (!p2pclient.isAlive())
                                                {
                                                    break;
                                                }
                                                rest = ifo.Length - cd;
                                                if (rest <= txBuff)
                                                {

                                                    mvs.ReadArray(cd, filebuff, 0, (int)rest);
                                                    if (!p2pclient.sendAll(filebuff, (int)rest))
                                                    {
                                                        break;
                                                    }
                                                    cd += rest;
                                                }
                                                else
                                                {
                                                    mvs.ReadArray(cd, filebuff, 0, txBuff);
                                                    if (!p2pclient.sendAll(filebuff))
                                                    {
                                                        break;
                                                    }
                                                    cd += txBuff;
                                                }
                                                if (sendFileRate != null)
                                                {
                                                    var dt = cd;
                                                    Task.Factory.StartNew(() =>
                                                    {
                                                        var rate = dt / (double)ifo.Length;
                                                        sendFileRate(this, new fileArg { endp = deIP, fileName = filename, fileState = fileType.send, str = rate.ToString("p2"), value = rate, userToken = inusertoken });
                                                    });
                                                }
                                                if (sendFileSpeed != null)
                                                {
                                                    if (spd == 0 || (spd > 0 && stp.Elapsed.TotalSeconds >= 1))
                                                    {
                                                        var dt = cd - spd;
                                                        spd = cd;
                                                        var sj = stp.Elapsed.TotalSeconds;
                                                        Task.Factory.StartNew(() =>
                                                        {
                                                            var speed = dt / sj;
                                                            sendFileSpeed(this, new fileArg { endp = deIP, fileName = filename, fileState = fileType.send, str = speed.fileSize() + "/s", value = speed, userToken = inusertoken });
                                                        });
                                                        stp.Restart();
                                                    }
                                                }
                                                if (fileDelay > 0)
                                                {
                                                    Thread.Sleep(fileDelay);
                                                }

                                            }
                                            stp.Stop();
                                            if (cd != ifo.Length)
                                            {
                                                callEvent(sendOver, new netArg(deIP, true, false, filename, inusertoken));
                                            }
                                            else
                                            {
                                                callEvent(sendOver, new netArg(deIP, true, true, filename, inusertoken));
                                            }
                                        }
                                    }

                                }
                                else
                                {
                                    callEvent(sendOver, new netArg(deIP, true, false, filename, inusertoken));
                                }
                            }
                            else
                            {
                                callEvent(sendOver, new netArg(deIP, true, false, filename, inusertoken));
                            }


                        }
                        else
                        {
                            callEvent(sendOver, new netArg(deIP, true, false, filename, inusertoken));
                        }
                    }
                    else
                    {
                        callEvent(sendOver, new netArg(deIP, true, false, filename, inusertoken));
                    }

                }
                catch (Exception ex)
                {
                    callEvent(sendOver, new netArg(deIP, true, false, filename, inusertoken));
                    if (!ownClose)
                    {
                        callEvent(errorRec, new infoArg { iType = infoType.cli_sendfileP2P, message = ex.Message });
                    }

                }
                finally
                {
                    if (p2pclient != null)
                    {
                        sktLst.TryRemove(((IPEndPoint)p2pclient.LocalEndPoint).toLong(), out var tp);
                        p2pclient.safeClose();
                    }
                }
            }
        }
        public void disConnect()
        {
            close(true);
        }
        private void close(bool inownclose=false)
        {
            try
            {
                if (inownclose)
                {
                    ownClose = inownclose;
                }

                foreach (var item in sktLst.Values)
                {
                    if (item!=null)
                    {
                        item.safeClose();
                    }
                }
                if (p2pSocket != null)
                {
                    p2pSocket.safeClose();
                }
                if (workSocket != null)
                {
                    workSocket.safeClose();
                }
                iplock.EnterWriteLock();
                lcEDP = null;
                iplock.ExitWriteLock();
            }
            catch
            {
#if debug
                throw;
#endif
                ownClose = inownclose;

                foreach (var item in sktLst.Values)
                {
                    if (item != null)
                    {
                        item.safeClose();
                    }
                }
                if (p2pSocket != null)
                {
                    p2pSocket.safeClose();
                }
                if (workSocket != null)
                {
                    workSocket.safeClose();
                }

                iplock.EnterWriteLock();
                lcEDP = null;
                iplock.ExitWriteLock();
                
            }
        }

    }
    public static class ex
    {
        public static byte[] SerializeToBin(this object inobj,bool compress=true)
        {
            if (compress)
            {
                return HproseFormatter.Serialize(inobj).ToArray().DEFcompress();
            }
            else
            {
                return HproseFormatter.Serialize(inobj).ToArray();
            }
            
        }
        public static T UnserializeFromBin<T>(this byte[] inbin,bool decompress=true)
        {
            if (decompress)
            {
                return HproseFormatter.Unserialize<T>(inbin.DEFdecompress());
            }
            else
            {
                return HproseFormatter.Unserialize<T>(inbin);
            }
            
        }
        public static byte[] DEFcompress(this byte[] inbin)
        {
            var ms = new MemoryStream();
            var zip = new DeflateStream(ms, CompressionMode.Compress);
            zip.Write(inbin, 0, inbin.Length);
            zip.Close();
            return ms.ToArray();
        }
        public static byte[] DEFdecompress(this byte[] inbin)
        {
            var ms = new MemoryStream();
            var zip = new DeflateStream(new MemoryStream(inbin), CompressionMode.Decompress);
            zip.CopyTo(ms);
            zip.Close();
            return ms.ToArray();
        }
        public static byte[] GZIPcompress(this byte[] inbin)
        {
            var ms = new MemoryStream();
            var zip = new GZipStream(ms, CompressionMode.Compress);
            zip.Write(inbin, 0, inbin.Length);
            zip.Close();
            return ms.ToArray();
        }
        public static byte[] GZIPdecompress(this byte[] inbin)
        {
            var ms = new MemoryStream();
            var zip = new GZipStream(new MemoryStream(inbin), CompressionMode.Decompress);
            zip.CopyTo(ms);
            zip.Close();
            return ms.ToArray();
        }

    }



}
